package xiao_jin.fantastic_journey.core.mixin;

import club.someoneice.cookie.event.EventBus;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.fluid.Fluid;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.tag.TagKey;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import xiao_jin.fantastic_journey.api.mixin.IPCEntity;
import xiao_jin.fantastic_journey.core.handler.LivingEntityEvent;

@Mixin(LivingEntity.class)
public class LivingEntityMixin implements IPCEntity {
    @Unique
    NbtCompound customNbt;

    @Override
    public NbtCompound PC$getOrCreateNBTTagCompound() {
        return this.customNbt = this.customNbt == null ? new NbtCompound() : this.customNbt;
    }

    @Override
    public void PC$setNBTTagCompound(NbtCompound nbtCompound) {
        this.customNbt = nbtCompound;
    }

    @Inject(method = "writeCustomDataToNbt", at = @At("HEAD"))
    public void onWrite(NbtCompound nbt, CallbackInfo ci) {
        var nbtCompound = this.PC$getOrCreateNBTTagCompound();
        nbt.put("pc_custom", nbtCompound);
    }

    @Inject(method = "readCustomDataFromNbt", at = @At("HEAD"))
    public void onRead(NbtCompound nbt, CallbackInfo ci) {
        this.PC$setNBTTagCompound(nbt.getCompound("pc_custom"));
    }

    /* Event Hooks Start */
    @Inject(method = "onDamaged", at = @At("HEAD"), cancellable = true)
    public void onEntityDamaged(DamageSource damageSource, CallbackInfo ci) {
        final var thiz = (LivingEntity) (Object) this;
        final var isCanceled = EventBus.post(new LivingEntityEvent.LivingDamagedEvent(thiz, damageSource)).shouldCancel;
        if (isCanceled) ci.cancel();
    }

    @Inject(method = "modifyAppliedDamage", at = @At("RETURN"), cancellable = true)
    public void entityHurtModify(DamageSource source, float amount, CallbackInfoReturnable<Float> cir) {
        final var thiz = (LivingEntity) (Object) this;
        final var event = EventBus.post(new LivingEntityEvent.LivingHurtEvent(thiz, source, amount));
        amount = event.shouldCancel ? 0.0f : event.getAmount();

        cir.setReturnValue(amount);
    }

    @Inject(method = "swimUpward", at = @At("TAIL"))
    public void onEntitySwimUpward(TagKey<Fluid> fluidType, CallbackInfo ci) {
        final var thiz = (LivingEntity) (Object) this;

        final var pos = thiz.getBlockPos();
        final var world = thiz.getEntityWorld();
        final var fluidState = world.getFluidState(pos);
        final var jumpingCooldown = ((LivingEntityAccessor) thiz).getJumpingCooldown();

        LivingEntityEvent.LivingSwimUpawrdEvent event =
                EventBus.post(new LivingEntityEvent.LivingSwimUpawrdEvent(thiz, world, pos, fluidState, fluidType, jumpingCooldown));

        ((LivingEntityAccessor) thiz).setJumpingCooldown(Math.max(0, event.getJumpingCooldown()));
    }

    @Inject(method = "jump", at = @At("HEAD"))
    public void onEntityJumping(CallbackInfo ci) {
        final var thiz = (LivingEntity) (Object) this;

        final var world = thiz.getEntityWorld();
        LivingEntityEvent.LivingJumpingEvent event =
                EventBus.post(new LivingEntityEvent.LivingJumpingEvent(thiz, world));

        if (event.shouldCancel) ci.cancel();
    }
}
